#include "fileref.h"
#include "napi/native_api.h"
#include "tpropertymap.h"
#include "utils.cpp"
#include <memory>

static char *PathToUri(char *path) {
    unsigned int length = strlen(path);
    char *pathResult = NULL;
    FileManagement_ErrCode ret = OH_FileUri_GetPathFromUri(path, length, &pathResult);
    if (ret == 0 && pathResult != NULL) {
        return pathResult;
    }
    return nullptr;
}

napi_value StringListToJsArray(napi_env env, const TagLib::StringList &stringList) {
    uint32_t length = static_cast<uint32_t>(stringList.size());
    napi_value jsArray;
    napi_create_array_with_length(env, length, &jsArray);

    for (int i = 0; i < length; i++) {
        const TagLib::String &str = stringList[i];
        const char *cStr = str.toCString(true);
        napi_value jsString;
        napi_create_string_utf8(env, cStr, NAPI_AUTO_LENGTH, &jsString);
        napi_set_element(env, jsArray, i, jsString);
    }

    return jsArray;
}

static napi_value NAPI_Global_getAudioProperties(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1] = {nullptr};

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    size_t len = 0;
    napi_get_value_string_utf8(env, args[0], nullptr, 0, &len);
    const std::unique_ptr<char[]> file_path = std::make_unique<char[]>(len + 1);
    napi_get_value_string_utf8(env, args[0], file_path.get(), len + 1, &len);
    char *path = PathToUri(file_path.get());
    const TagLib::FileRef f(path);
    free(path);
    if (f.isNull()) {
        return nullptr;
    }
    const auto prop = f.audioProperties();
    prop->bitrate();
    prop->channels();
    prop->lengthInMilliseconds();
    prop->sampleRate();

    napi_value jsMap;
    napi_create_object(env, &jsMap);

    const int bitrate = prop->bitrate();
    const int channels = prop->channels();
    const int lengthMs = prop->lengthInMilliseconds();
    const int sampleRate = prop->sampleRate();

    napi_value jsLengthKey, jsLengthValue;
    napi_create_string_utf8(env, "length", NAPI_AUTO_LENGTH, &jsLengthKey);
    napi_create_int32(env, lengthMs, &jsLengthValue);
    napi_set_property(env, jsMap, jsLengthKey, jsLengthValue);

    napi_value jsBitrateKey, jsBitrateValue;
    napi_create_string_utf8(env, "bitrate", NAPI_AUTO_LENGTH, &jsBitrateKey);
    napi_create_int32(env, bitrate, &jsBitrateValue);
    napi_set_property(env, jsMap, jsBitrateKey, jsBitrateValue);

    napi_value jsSampleRateKey, jsSampleRateValue;
    napi_create_string_utf8(env, "sampleRate", NAPI_AUTO_LENGTH, &jsSampleRateKey);
    napi_create_int32(env, sampleRate, &jsSampleRateValue);
    napi_set_property(env, jsMap, jsSampleRateKey, jsSampleRateValue);

    napi_value jsChannelsKey, jsChannelsValue;
    napi_create_string_utf8(env, "channels", NAPI_AUTO_LENGTH, &jsChannelsKey);
    napi_create_int32(env, channels, &jsChannelsValue);
    napi_set_property(env, jsMap, jsChannelsKey, jsChannelsValue);

    return jsMap;
}

static napi_value NAPI_Global_getMetadata(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1] = {nullptr};

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    size_t len = 0;
    napi_get_value_string_utf8(env, args[0], nullptr, 0, &len);
    const std::unique_ptr<char[]> file_path = std::make_unique<char[]>(len + 1);
    napi_get_value_string_utf8(env, args[0], file_path.get(), len + 1, &len);
    char *path = PathToUri(file_path.get());
    const TagLib::FileRef f(path);
    free(path);
    if (f.isNull()) {
        return nullptr;
    }
    const auto prop = f.properties();
    napi_value jsMetadataMap;
    napi_create_object(env, &jsMetadataMap);
    for (const auto &property : prop) {
        const char *key = property.first.toCString(true);
        const auto &valueList = property.second;
        const auto array = StringListToJsArray(env, valueList);
        napi_value jsKey;
        napi_create_string_utf8(env, key, NAPI_AUTO_LENGTH, &jsKey);
        napi_set_property(env, jsMetadataMap, jsKey, array);
    }
    napi_value jsPictureArray;
    napi_create_array(env, &jsPictureArray);
    const auto pictureProp = f.complexProperties("PICTURE");
    for (int i = 0; i < pictureProp.size(); i++) {
        const auto picture = pictureProp[i];
        const auto pictureData = picture["data"].toByteVector();
        if (pictureData.isEmpty()) {
            continue;
        }
        napi_value jsPictureObj;
        napi_create_object(env, &jsPictureObj);
        const auto length = pictureData.size();
        napi_value arraybuffer;
        void *buffer_data;
        napi_create_arraybuffer(env, length, &buffer_data, &arraybuffer);
        memcpy(buffer_data, pictureData.data(), length);
        napi_value jsPictureInt8Array;
        napi_create_typedarray(env, napi_int8_array, length, arraybuffer, 0, &jsPictureInt8Array);
        napi_value jsByteKey;
        napi_create_string_utf8(env, "data", NAPI_AUTO_LENGTH, &jsByteKey);
        napi_set_property(env, jsPictureObj, jsByteKey, jsPictureInt8Array);

        napi_value jsDescriptionKey;
        napi_create_string_utf8(env, "description", NAPI_AUTO_LENGTH, &jsDescriptionKey);
        const auto descriptionStr = picture["description"].toString().toCString();
        napi_value jsDescription;
        napi_create_string_utf8(env, descriptionStr, NAPI_AUTO_LENGTH, &jsDescription);
        napi_set_property(env, jsPictureObj, jsDescriptionKey, jsDescription);

        napi_value jsPictureTypeKey;
        napi_create_string_utf8(env, "pictureType", NAPI_AUTO_LENGTH, &jsPictureTypeKey);
        const auto pictureTypeStr = picture["pictureType"].toString().toCString();
        napi_value jsPictureType;
        napi_create_string_utf8(env, pictureTypeStr, NAPI_AUTO_LENGTH, &jsPictureType);
        napi_set_property(env, jsPictureObj, jsPictureTypeKey, jsPictureType);

        napi_value jsMimeTypeKey;
        napi_create_string_utf8(env, "mimeType", NAPI_AUTO_LENGTH, &jsMimeTypeKey);
        const auto mimeTypeStr = picture["mimeType"].toString().toCString();
        napi_value jsMimeType;
        napi_create_string_utf8(env, mimeTypeStr, NAPI_AUTO_LENGTH, &jsMimeType);
        napi_set_property(env, jsPictureObj, jsMimeTypeKey, jsMimeType);

        napi_set_element(env, jsPictureArray, i, jsPictureObj);
    }
    napi_value jsPictureKey;
    napi_create_string_utf8(env, "PICTURE", NAPI_AUTO_LENGTH, &jsPictureKey);
    napi_set_property(env, jsMetadataMap, jsPictureKey, jsPictureArray);

    return jsMetadataMap;
}

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
    napi_property_descriptor desc[] = {
        {"getAudioProperties", nullptr, NAPI_Global_getAudioProperties, nullptr, nullptr, nullptr, napi_default,
         nullptr},
        {"getMetadata", nullptr, NAPI_Global_getMetadata, nullptr, nullptr, nullptr, napi_default, nullptr}};
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "taglib",
    .nm_priv = ((void *)0),
    .reserved = {0},
};

extern "C" __attribute__((constructor)) void RegisterLibraryModule(void) { napi_module_register(&demoModule); }